package com.gitee.sparta.core.cluster.netty.handler;

import com.gitee.sparta.core.bean.cluster.ClusterCmd;
import com.gitee.sparta.core.bean.cluster.ClusterProtocol;
import com.gitee.sparta.core.cluster.ClusterCache;
import com.gitee.sparta.core.cluster.balance.LoadBalance;
import com.gitee.sparta.core.cluster.strategy.Strategy;
import com.gitee.sparta.core.cluster.strategy.StrategyFactory;
import com.gitee.sparta.core.property.SpartaCoreProperties;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RedissonClient;

import java.util.Objects;

/**
 * <p>
 *      处理 clusterProtocol 对象处理器
 * </p>
 *
 * @author zyred
 * @since v 0.0.2
 */
@Slf4j
@ChannelHandler.Sharable
public class LeaderProtocolHandler extends SimpleChannelInboundHandler<ClusterProtocol> {

    protected final SpartaCoreProperties properties;
    protected final RedissonClient client;
    protected final LoadBalance loadBalance;


    public LeaderProtocolHandler(SpartaCoreProperties properties,
                                 RedissonClient client,
                                 LoadBalance loadBalance) {
        this.properties = properties;
        this.client = client;
        this.loadBalance = loadBalance;
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        log.info("[Sparta] Channel active, remote address: {}", ctx.channel().remoteAddress());
        super.channelActive(ctx);
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, ClusterProtocol cp) {
        Strategy strategy = StrategyFactory.getStrategy(cp.getCmd());
        if (Objects.isNull(strategy)) {
            log.info("[Sparta] Strategy not found, cmd: {}", cp.getCmd());
            return;
        }
        strategy.handle(ctx, null, cp);
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) {
        // 当 follower 断开连接的时候, 可能会触发两个地方调用
        // 1. 本方法的调用: 由 netty 自动调用,
        // 2. FollowerShutdown#handle 方法的调用

        // 如果先执行了本方法, 那么一定会拿到 sid 值, 如果
        // 先收到 follower 的关闭消息,那么先执行 FollowerShutdown#handle
        // handle 方法中从 ClusterCache 中移除了 follower 连接
        // 所以这里肯定那不到内容,就会报空指针异常
        Integer sid = ClusterCache.getSid(ctx.channel());
        if (Objects.isNull(sid)) {
            return;
        }

        Strategy strategy = StrategyFactory.getStrategy(ClusterCmd.FOLLOWER_SHUTDOWN);
        ClusterProtocol protocol = new ClusterProtocol();
        protocol.setServerId(sid);
        strategy.handle(ctx, null, protocol);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        ctx.channel().close();
        log.error("[Sparta] Leader handler, exception caught", cause);
    }
}
